#pragma once
#include "ODrawCallback.h"
#include "Config.h"

class OCamera : public osg::Camera {
public:
	/**@param viewerIniCamera this is osgViewer::Viewer's default initial camera(global camera containing the basic arguments/status) */
	OCamera(osg::Camera* viewerIniCamera): osg::Camera(*viewerIniCamera) {
		init();

		auto gc = viewerIniCamera->getGraphicsContext();
		const auto* glEXT = gc->getState()->get<osg::GLExtensions>();
		const auto* trait = gc->getTraits();
		LogI << ">>> VSYNC: " << trait->vsync << ", DoubleBuffer: " << trait->doubleBuffer;
		setGraphicsContext(gc);// because the GraphicsContext Object not and can't be copyed!
	}

	/***@brief render to current main surface; false:this camera use as a sub graph node, render to FBO as texture(known as RTT) */
	OCamera() {
		init();

		setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
		setRenderOrder(osg::Camera::PRE_RENDER);
		auto fboImage = new OImage(W3D, H3D);
		attach(osg::Camera::COLOR_BUFFER, fboImage);

		setPostDrawCallback(new ODrawCallback(fboImage));
	}

	~OCamera() { std::cout << ">>> ~OCamera" << std::endl; }

	void setPosX(float x) { if (m_position.x == x) return; m_position.x = x; calViewMatrix(); }
	void setPosY(float y) { if (m_position.y == y) return; m_position.y = y; calViewMatrix(); }
	void setPosZ(float z) { if (m_position.z == z) return; m_position.z = z; calViewMatrix(); }
	void setPitchAngle(float pitchAngle) { auto r = CMath::radians(pitchAngle); if (m_pitchAngle == r) return; m_pitchAngle = r; calViewMatrix(); }// unit:radian
	void setFov(float fov) { auto r = CMath::radians(fov); if (m_fov == fov) return; m_fov = r; calProjectionMatrix(); }// unit:radian
	void setYawAngle(float yawAngle) { auto r = CMath::radians(yawAngle); if (m_yawAngle == r) return; m_yawAngle = r; calViewMatrix(); }// unit:radian
	void setViewScale(float viewScale) { if (m_viewScale == viewScale) return; m_viewScale = viewScale; calViewMatrix(); }

	void resetAll() {
		resetX();
		resetY();
		resetZ();
		resetPitchAngle();
		resetFov();
		resetYawAngle();
		resetViewScale();
	}

	void resetX() { setPosX(Config::Camera_Position.x); }
	void resetY() { setPosY(Config::Camera_Position.y); }
	void resetZ() { setPosZ(Config::Camera_Position.z); }
	void resetPitchAngle() { setPitchAngle(Config::Camera_PitchAngle); }
	void resetFov() { setFov(Config::Camera_FOV); }
	void resetYawAngle() { setYawAngle(Config::Camera_YawAngle); }
	void resetViewScale() { setViewScale(Config::Camera_ViewScale); }

private:
	void init() {
		setReferenceFrame(osg::Camera::ABSOLUTE_RF);// relative to the world coordinate system (absolute coordinate system)
		setClearColor(osg::Vec4(0.0, 0.0, 0.0, 0.0));// transparent

		calViewMatrix();
		calProjectionMatrix();
	}

	void calViewMatrix();

	void calProjectionMatrix();

	osg::Vec3d calculateWorldCoordinate(float screenX, float screenY);

	P3D m_position = Config::Camera_Position;
	float m_pitchAngle = CMath::radians(Config::Camera_PitchAngle);// radians
	float m_fov = CMath::radians(Config::Camera_FOV);// radians
	float m_yawAngle = CMath::radians(Config::Camera_YawAngle);// radians
	float m_viewScale = Config::Camera_ViewScale;

	glm::mat4 m_viewMatrix = Mat_Zero, m_projectionMatrix = Mat_Zero;
};